home *** CD-ROM | disk | FTP | other *** search
/ PC World 2008 February (DVD) / PCWorld_2008-02_DVD.iso / v cisle / PHP / PHP.exe / xampp-win32-1.6.5-installer.exe / php / PEAR / phing / Phing.php < prev    next >
Encoding:
PHP Script  |  2007-12-20  |  41.4 KB  |  1,162 lines

  1. <?php
  2. /*
  3.  * $Id: Phing.php,v 1.51 2006/01/06 15:12:33 hlellelid Exp $
  4.  *
  5.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  6.  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  7.  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  8.  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  9.  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  10.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  11.  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  12.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  13.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  14.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  15.  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  16.  *
  17.  * This software consists of voluntary contributions made by many individuals
  18.  * and is licensed under the LGPL. For more information please see
  19.  * <http://phing.info>.
  20.  */
  21.  
  22. require_once 'phing/Project.php';
  23. require_once 'phing/ProjectComponent.php';
  24. require_once 'phing/Target.php';
  25. require_once 'phing/Task.php';
  26.  
  27. include_once 'phing/BuildException.php';
  28. include_once 'phing/BuildEvent.php';
  29.  
  30. include_once 'phing/parser/Location.php';
  31. include_once 'phing/parser/ExpatParser.php';
  32. include_once 'phing/parser/AbstractHandler.php';
  33. include_once 'phing/parser/ProjectConfigurator.php';
  34. include_once 'phing/parser/RootHandler.php';
  35. include_once 'phing/parser/ProjectHandler.php';
  36. include_once 'phing/parser/TaskHandler.php';
  37. include_once 'phing/parser/TargetHandler.php';
  38. include_once 'phing/parser/DataTypeHandler.php';
  39. include_once 'phing/parser/NestedElementHandler.php';
  40.  
  41. include_once 'phing/system/util/Properties.php';
  42. include_once 'phing/util/StringHelper.php';
  43. include_once 'phing/system/io/PhingFile.php';
  44. include_once 'phing/system/io/FileReader.php';
  45. include_once 'phing/system/util/Register.php';
  46.  
  47. /**
  48.  * Entry point into Phing.  This class handles the full lifecycle of a build -- from 
  49.  * parsing & handling commandline arguments to assembling the project to shutting down
  50.  * and cleaning up in the end.
  51.  *
  52.  * If you are invoking Phing from an external application, this is still
  53.  * the class to use.  Your applicaiton can invoke the start() method, passing
  54.  * any commandline arguments or additional properties.
  55.  *
  56.  * @author    Andreas Aderhold <andi@binarycloud.com>
  57.  * @author    Hans Lellelid <hans@xmpl.org>
  58.  * @version   $Revision: 1.51 $
  59.  * @package   phing
  60.  */
  61. class Phing {
  62.  
  63.     /** The default build file name */
  64.     const DEFAULT_BUILD_FILENAME = "build.xml";
  65.  
  66.     /** Our current message output status. Follows PROJECT_MSG_XXX */
  67.     private static $msgOutputLevel = PROJECT_MSG_INFO;
  68.  
  69.     /** PhingFile that we are using for configuration */
  70.     private $buildFile = null;
  71.  
  72.     /** The build targets */
  73.     private $targets = array();
  74.  
  75.     /**
  76.      * Set of properties that are passed in from commandline or invoking code.
  77.      * @var Properties
  78.      */
  79.     private static $definedProps;
  80.  
  81.     /** Names of classes to add as listeners to project */
  82.     private $listeners = array();
  83.  
  84.     private $loggerClassname = null;
  85.  
  86.     /** The class to handle input (can be only one). */
  87.     private $inputHandlerClassname;
  88.     
  89.     /** Indicates if this phing should be run */
  90.     private $readyToRun = false;
  91.  
  92.     /** Indicates we should only parse and display the project help information */
  93.     private $projectHelp = false;
  94.     
  95.     /** Used by utility function getResourcePath() */
  96.     private static $importPaths;
  97.     
  98.     /** System-wide static properties (moved from System) */
  99.     private static $properties = array();
  100.     
  101.     /** Static system timer. */
  102.     private static $timer;
  103.     
  104.     /** The current Project */
  105.     private static $currentProject;
  106.     
  107.     /** Whether to capture PHP errors to buffer. */
  108.     private static $phpErrorCapture = false;
  109.     
  110.     /** Array of captured PHP errors */
  111.     private static $capturedPhpErrors = array();
  112.     
  113.     /**
  114.      * Prints the message of the Exception if it's not null.
  115.      */
  116.     function printMessage(Exception $t) {
  117.         print($t->getMessage() . "\n");
  118.         if (self::getMsgOutputLevel() === PROJECT_MSG_DEBUG) {
  119.             print($t->getTraceAsString()."\n");
  120.             if ($t instanceof Exception) {                
  121.                 $c = $t->getCause();
  122.                 if ($c !== null) {
  123.                     print("Wrapped exception trace:\n");
  124.                     print($c->getTraceAsString() . "\n");
  125.                 }
  126.             }
  127.         } // if output level is DEBUG
  128.     }
  129.  
  130.     /** 
  131.      * Entry point allowing for more options from other front ends.
  132.      * 
  133.      * This method encapsulates the complete build lifecycle.
  134.      * 
  135.      * @param array &$args The commandline args passed to phing shell script.
  136.      * @param array $additionalUserProperties   Any additional properties to be passed to Phing (alternative front-end might implement this).
  137.      *                                          These additional properties will be available using the getDefinedProperty() method and will
  138.      *                                          be added to the project's "user" properties.
  139.      * @return void
  140.      * @see execute()
  141.      * @see runBuild()
  142.      */
  143.     public static function start(&$args, $additionalUserProperties = null) {
  144.  
  145.         try {
  146.             $m = new Phing();
  147.             $m->execute($args);
  148.         } catch (Exception $exc) {
  149.             $m->printMessage($exc);
  150.             self::halt(-1); // Parameter error
  151.         }
  152.  
  153.         if ($additionalUserProperties !== null) {
  154.             $keys = $m->additionalUserProperties->keys();
  155.             while(count($keys)) {
  156.                 $key = array_shift($keys);
  157.                 $property = $m->additionalUserProperties->getProperty($key);
  158.                 $m->setDefinedProperty($key, $property);
  159.             }
  160.         }
  161.  
  162.         try {
  163.             $m->runBuild();
  164.         } catch(Exception $exc) {
  165.             self::halt(1); // Errors occured
  166.         }
  167.         
  168.         // everything fine, shutdown
  169.         self::halt(0); // no errors, everything is cake
  170.     }
  171.     
  172.     /**
  173.      * Making output level a static property so that this property
  174.      * can be accessed by other parts of the system, enabling
  175.      * us to display more information -- e.g. backtraces -- for "debug" level.
  176.      * @return int
  177.      */
  178.     public static function getMsgOutputLevel() {
  179.         return self::$msgOutputLevel;
  180.     }        
  181.     
  182.     /**
  183.      * Command line entry point. This method kicks off the building
  184.      * of a project object and executes a build using either a given
  185.      * target or the default target.
  186.      *
  187.      * @param array $args Command line args.
  188.      * @return void
  189.      */
  190.     public static function fire($args) {
  191.         self::start($args, null);
  192.     }
  193.     
  194.     /**
  195.      * Setup/initialize Phing environment from commandline args.
  196.      * @param array $args commandline args passed to phing shell.
  197.      * @return void
  198.      */
  199.     public function execute($args) {
  200.     
  201.         self::$definedProps = new Properties();
  202.         $this->searchForThis = null;
  203.  
  204.         // cycle through given args
  205.         for ($i = 0, $argcount = count($args); $i < $argcount; ++$i) { 
  206.                             // ++$i intentional here, as first param is script name
  207.             $arg = $args[$i];
  208.  
  209.             if ($arg == "-help" || $arg == "-h") {
  210.                 $this->printUsage();
  211.                 return;
  212.             } elseif ($arg == "-version" || $arg == "-v") {
  213.                 $this->printVersion();
  214.                 return;
  215.             } elseif ($arg == "-quiet" || $arg == "-q") {
  216.                 self::$msgOutputLevel = PROJECT_MSG_WARN;
  217.             } elseif ($arg == "-verbose") {
  218.                 $this->printVersion();
  219.                 self::$msgOutputLevel = PROJECT_MSG_VERBOSE;
  220.             } elseif ($arg == "-debug") {
  221.                 $this->printVersion();
  222.                 self::$msgOutputLevel = PROJECT_MSG_DEBUG;
  223.             } elseif ($arg == "-logfile") {
  224.                 try { // try to set logfile
  225.                     if (!isset($args[$i+1])) {
  226.                         print("You must specify a log file when using the -logfile argument\n");
  227.                         return;
  228.                     } else {
  229.                         $logFile = new PhingFile($args[++$i]);
  230.                         $this->loggerClassname = 'phing.listener.PearLogger';
  231.                         $this->setDefinedProperty('pear.log.name', $logFile->getAbsolutePath());
  232.                     }
  233.                 } catch (IOException $ioe) {
  234.                     print("Cannot write on the specified log file. Make sure the path exists and you have write permissions.\n");
  235.                     throw $ioe;
  236.                 }
  237.             } elseif ($arg == "-buildfile" || $arg == "-file" || $arg == "-f") {
  238.                 if (!isset($args[$i+1])) {
  239.                     print("You must specify a buildfile when using the -buildfile argument\n");
  240.                     return;
  241.                 } else {
  242.                     $this->buildFile = new PhingFile($args[++$i]);
  243.                 }
  244.             } elseif ($arg == "-listener") {
  245.                 if (!isset($args[$i+1])) {
  246.                     print("You must specify a listener class when using the -listener argument\n");
  247.                     return;
  248.                 } else {
  249.                     $this->listeners[] = $args[++$i];
  250.                 }
  251.                 
  252.             } elseif (StringHelper::startsWith("-D", $arg)) {
  253.                 $name = substr($arg, 2);
  254.                 $value = null;
  255.                 $posEq = strpos($name, "=");
  256.                 if ($posEq !== false) {
  257.                     $value = substr($name, $posEq+1);
  258.                     $name  = substr($name, 0, $posEq);
  259.                 } elseif ($i < count($args)-1) {
  260.                     $value = $args[++$i];
  261.                 }
  262.                 self::$definedProps->setProperty($name, $value);
  263.             } elseif ($arg == "-logger") {
  264.                 if (!isset($args[$i+1])) {
  265.                     print("You must specify a classname when using the -logger argument\n");
  266.                     return;
  267.                 } else {
  268.                     $this->loggerClassname = $args[++$i];
  269.                 }
  270.             } elseif ($arg == "-inputhandler") {
  271.                 if ($this->inputHandlerClassname !== null) {
  272.                     throw new BuildException("Only one input handler class may be specified.");
  273.                 }
  274.                 if (!isset($args[$i+1])) {
  275.                     print("You must specify a classname when using the -inputhandler argument\n");
  276.                     return;
  277.                 } else {
  278.                     $this->inputHandlerClassname = $args[++$i];
  279.                 }
  280.             } elseif ($arg == "-projecthelp" || $arg == "-targets" || $arg == "-list" || $arg == "-l") {
  281.                 // set the flag to display the targets and quit
  282.                 $this->projectHelp = true;
  283.             } elseif ($arg == "-find") {
  284.                 // eat up next arg if present, default to build.xml
  285.                 if ($i < count($args)-1) {
  286.                     $this->searchForThis = $args[++$i];
  287.                 } else {
  288.                     $this->searchForThis = self::DEFAULT_BUILD_FILENAME;
  289.                 }
  290.             } elseif (substr($arg,0,1) == "-") {
  291.                 // we don't have any more args
  292.                 print("Unknown argument: $arg\n");
  293.                 $this->printUsage();
  294.                 return;
  295.             } else {
  296.                 // if it's no other arg, it may be the target
  297.                 array_push($this->targets, $arg);
  298.             }
  299.         }
  300.  
  301.         // if buildFile was not specified on the command line,
  302.         if ($this->buildFile === null) {
  303.             // but -find then search for it
  304.             if ($this->searchForThis !== null) {
  305.                 $this->buildFile = $this->_findBuildFile(self::getProperty("user.dir"), $this->searchForThis);
  306.             } else {
  307.                 $this->buildFile = new PhingFile(self::DEFAULT_BUILD_FILENAME);
  308.             }
  309.         }
  310.         // make sure buildfile exists
  311.         if (!$this->buildFile->exists()) {
  312.             throw new BuildException("Buildfile: " . $this->buildFile->__toString() . " does not exist!");
  313.         }
  314.  
  315.         // make sure it's not a directory
  316.         if ($this->buildFile->isDirectory()) {   
  317.             throw new BuildException("Buildfile: " . $this->buildFile->__toString() . " is a dir!");
  318.         }
  319.  
  320.         $this->readyToRun = true;
  321.     }
  322.  
  323.     /**
  324.      * Helper to get the parent file for a given file.
  325.      *
  326.      * @param PhingFile $file
  327.      * @return PhingFile Parent file or null if none
  328.      */
  329.     function _getParentFile(PhingFile $file) {
  330.         $filename = $file->getAbsolutePath();
  331.         $file     = new PhingFile($filename);
  332.         $filename = $file->getParent();
  333.  
  334.         if ($filename !== null && self::$msgOutputLevel >= PROJECT_MSG_VERBOSE) {
  335.             print("Searching in $filename\n");
  336.         }
  337.  
  338.         return ($filename === null) ? null : new PhingFile($filename);
  339.     }
  340.  
  341.     /**
  342.      * Search parent directories for the build file.
  343.      *
  344.      * Takes the given target as a suffix to append to each
  345.      * parent directory in search of a build file.  Once the
  346.      * root of the file-system has been reached an exception
  347.      * is thrown.
  348.      * 
  349.      * @param string $start Start file path.
  350.      * @param string $suffix Suffix filename to look for in parents.
  351.      * @return PhingFile A handle to the build file
  352.      *
  353.      * @throws BuildException    Failed to locate a build file
  354.      */
  355.     function _findBuildFile($start, $suffix) {
  356.         if (self::$msgOutputLevel >= PROJECT_MSG_INFO) {
  357.             print("Searching for $suffix ...\n");
  358.         }
  359.         $startf = new PhingFile($start);
  360.         $parent = new PhingFile($startf->getAbsolutePath());
  361.         $file   = new PhingFile($parent, $suffix);
  362.  
  363.         // check if the target file exists in the current directory
  364.         while (!$file->exists()) {
  365.             // change to parent directory
  366.             $parent = $this->_getParentFile($parent);
  367.  
  368.             // if parent is null, then we are at the root of the fs,
  369.             // complain that we can't find the build file.
  370.             if ($parent === null) {
  371.                 throw new BuildException("Could not locate a build file!");
  372.             }
  373.             // refresh our file handle
  374.             $file = new PhingFile($parent, $suffix);
  375.         }
  376.         return $file;
  377.     }
  378.  
  379.     /**
  380.      * Executes the build.
  381.      * @return void
  382.      */
  383.     function runBuild() {
  384.  
  385.         if (!$this->readyToRun) {
  386.             return;
  387.         }
  388.         
  389.         $project = new Project();
  390.         
  391.         self::setCurrentProject($project);
  392.         set_error_handler(array('Phing', 'handlePhpError'));
  393.         
  394.         $error = null;
  395.  
  396.         $this->addBuildListeners($project);
  397.         $this->addInputHandler($project);
  398.         
  399.         // set this right away, so that it can be used in logging.
  400.         $project->setUserProperty("phing.file", $this->buildFile->getAbsolutePath());
  401.  
  402.         try {
  403.             $project->fireBuildStarted();
  404.             $project->init();
  405.         } catch (Exception $exc) {
  406.             $project->fireBuildFinished($exc);
  407.             throw $exc;        
  408.         }
  409.  
  410.         $project->setUserProperty("phing.version", $this->getPhingVersion());
  411.  
  412.         $e = self::$definedProps->keys();
  413.         while (count($e)) {
  414.             $arg   = (string) array_shift($e);
  415.             $value = (string) self::$definedProps->getProperty($arg);
  416.             $project->setUserProperty($arg, $value);
  417.         }
  418.         unset($e);
  419.  
  420.         $project->setUserProperty("phing.file", $this->buildFile->getAbsolutePath());
  421.  
  422.         // first use the Configurator to create the project object
  423.         // from the given build file.
  424.                 
  425.         try {
  426.             ProjectConfigurator::configureProject($project, $this->buildFile);
  427.         } catch (Exception $exc) {
  428.             $project->fireBuildFinished($exc);
  429.             restore_error_handler();
  430.             self::unsetCurrentProject();
  431.             throw $exc;
  432.         }         
  433.  
  434.         // make sure that we have a target to execute
  435.         if (count($this->targets) === 0) {
  436.             $this->targets[] = $project->getDefaultTarget();
  437.         }
  438.  
  439.         // execute targets if help param was not given
  440.         if (!$this->projectHelp) {
  441.             
  442.             try { 
  443.                 $project->executeTargets($this->targets);
  444.             } catch (Exception $exc) {
  445.                 $project->fireBuildFinished($exc);
  446.                 restore_error_handler();
  447.                 self::unsetCurrentProject();
  448.                 throw $exc;
  449.             }
  450.         }
  451.         // if help is requested print it
  452.         if ($this->projectHelp) {
  453.             try {
  454.                 $this->printDescription($project);
  455.                 $this->printTargets($project);
  456.             } catch (Exception $exc) {
  457.                 $project->fireBuildFinished($exc);
  458.                 restore_error_handler();
  459.                 self::unsetCurrentProject();
  460.                 throw $exc;
  461.             }
  462.         }
  463.                 
  464.         // finally {
  465.         if (!$this->projectHelp) {
  466.             $project->fireBuildFinished(null);
  467.         }
  468.         
  469.         restore_error_handler();
  470.         self::unsetCurrentProject();
  471.     }
  472.     
  473.     /**
  474.      * Bind any default build listeners to this project.
  475.      * Currently this means adding the logger.
  476.      * @param Project $project
  477.      * @return void
  478.      */
  479.     private function addBuildListeners(Project $project) {
  480.         // Add the default listener
  481.         $project->addBuildListener($this->createLogger());
  482.     }
  483.     
  484.     /**
  485.      * Creates the InputHandler and adds it to the project.
  486.      *
  487.      * @param Project $project the project instance.
  488.      *
  489.      * @throws BuildException if a specified InputHandler
  490.      *                           class could not be loaded.
  491.      */
  492.     private function addInputHandler(Project $project) {
  493.         if ($this->inputHandlerClassname === null) {
  494.             $handler = new DefaultInputHandler();
  495.         } else {
  496.             try {
  497.                 $clz = Phing::import($this->inputHandlerClassname);
  498.                 $handler = new $clz();
  499.                 if ($project !== null && method_exists($handler, 'setProject')) {
  500.                     $handler->setProject($project);
  501.                 } 
  502.             } catch (Exception $e) {
  503.                 $msg = "Unable to instantiate specified input handler "
  504.                     . "class " . $this->inputHandlerClassname . " : "
  505.                     . $e->getMessage();
  506.                 throw new BuildException($msg);
  507.             }
  508.         }
  509.         $project->setInputHandler($handler);
  510.     }
  511.  
  512.     /**
  513.      * Creates the default build logger for sending build events to the log.
  514.      * @return BuildListener The created Logger
  515.      */
  516.     private function createLogger() {
  517.         if ($this->loggerClassname !== null) {
  518.             self::import($this->loggerClassname);
  519.             // get class name part            
  520.             $classname = self::import($this->loggerClassname);
  521.             $logger = new $classname;
  522.         } else {
  523.             require_once 'phing/listener/DefaultLogger.php';
  524.             $logger = new DefaultLogger();
  525.         }
  526.         $logger->setMessageOutputLevel(self::$msgOutputLevel);
  527.         return $logger;
  528.     }
  529.     
  530.     /**
  531.      * Sets the current Project
  532.      * @param Project $p
  533.      */
  534.     public static function setCurrentProject($p) {
  535.         self::$currentProject = $p;
  536.     }
  537.     
  538.     /**
  539.      * Unsets the current Project
  540.      */
  541.     public static function unsetCurrentProject() {
  542.         self::$currentProject = null;
  543.     }
  544.     
  545.     /**
  546.      * Gets the current Project.
  547.      * @return Project Current Project or NULL if none is set yet/still.
  548.      */
  549.     public static function getCurrentProject() {
  550.         return self::$currentProject;
  551.     }
  552.     
  553.     /**
  554.      * A static convenience method to send a log to the current (last-setup) Project.
  555.      * If there is no currently-configured Project, then this will do nothing.
  556.      * @param string $message
  557.      * @param int $priority PROJECT_MSG_INFO, etc.
  558.      */
  559.     public static function log($message, $priority = PROJECT_MSG_INFO) {
  560.         $p = self::getCurrentProject();
  561.         if ($p) {
  562.             $p->log($message, $priority);
  563.         }
  564.     }
  565.     
  566.     /**
  567.      * Error handler for PHP errors encountered during the build.
  568.      * This uses the logging for the currently configured project.
  569.      */
  570.     public static function handlePhpError($level, $message, $file, $line) {
  571.         
  572.         // don't want to print supressed errors
  573.         if (error_reporting() > 0) {
  574.         
  575.             if (self::$phpErrorCapture) {
  576.             
  577.                 self::$capturedPhpErrors[] = array('message' => $message, 'level' => $level, 'line' => $line, 'file' => $file);
  578.                 
  579.             } else {
  580.             
  581.                 $message = '[PHP Error] ' . $message;
  582.                 $message .= ' [line ' . $line . ' of ' . $file . ']';
  583.         
  584.                 switch ($level) {
  585.                 
  586.                     case E_STRICT:
  587.                     case E_NOTICE:
  588.                     case E_USER_NOTICE:
  589.                         self::log($message, PROJECT_MSG_VERBOSE);
  590.                         break;
  591.                     case E_WARNING:
  592.                     case E_USER_WARNING:
  593.                         self::log($message, PROJECT_MSG_WARN);
  594.                         break;
  595.                     case E_ERROR:
  596.                     case E_USER_ERROR:
  597.                     default:
  598.                         self::log($message, PROJECT_MSG_ERR);
  599.     
  600.                 } // switch
  601.                 
  602.             } // if phpErrorCapture
  603.             
  604.         } // if not @
  605.         
  606.     }
  607.     
  608.     /**
  609.      * Begins capturing PHP errors to a buffer.
  610.      * While errors are being captured, they are not logged.
  611.      */
  612.     public static function startPhpErrorCapture() {
  613.         self::$phpErrorCapture = true;
  614.         self::$capturedPhpErrors = array();
  615.     }
  616.     
  617.     /**
  618.      * Stops capturing PHP errors to a buffer.
  619.      * The errors will once again be logged after calling this method.
  620.      */
  621.     public static function stopPhpErrorCapture() {
  622.         self::$phpErrorCapture = false;
  623.     }
  624.     
  625.     /**
  626.      * Clears the captured errors without affecting the starting/stopping of the capture.
  627.      */
  628.     public static function clearCapturedPhpErrors() {
  629.         self::$capturedPhpErrors = array();
  630.     }
  631.     
  632.     /**
  633.      * Gets any PHP errors that were captured to buffer.
  634.      * @return array array('message' => message, 'line' => line number, 'file' => file name, 'level' => error level)
  635.      */
  636.     public static function getCapturedPhpErrors() {
  637.         return self::$capturedPhpErrors;
  638.     }
  639.     
  640.     /**  Prints the usage of how to use this class */
  641.     function printUsage() {
  642.         $lSep = self::getProperty("line.separator");
  643.         $msg = "";
  644.         $msg .= "phing [options] [target [target2 [target3] ...]]" . $lSep;
  645.         $msg .= "Options: " . $lSep;
  646.         $msg .= "  -h -help               print this message" . $lSep;
  647.         $msg .= "  -l -list               list available targets in this project" . $lSep;
  648.         $msg .= "  -v -version            print the version information and exit" . $lSep;
  649.         $msg .= "  -q -quiet              be extra quiet" . $lSep;
  650.         $msg .= "  -verbose               be extra verbose" . $lSep;
  651.         $msg .= "  -debug                 print debugging information" . $lSep;
  652.         $msg .= "  -logfile <file>        use given file for log" . $lSep;
  653.         $msg .= "  -logger <classname>    the class which is to perform logging" . $lSep;
  654.         $msg .= "  -f -buildfile <file>   use given buildfile" . $lSep;
  655.         $msg .= "  -D<property>=<value>   use value for given property" . $lSep;
  656.         $msg .= "  -find <file>           search for buildfile towards the root of the" . $lSep;
  657.         $msg .= "                         filesystem and use it" . $lSep;
  658.         //$msg .= "  -recursive <file>      search for buildfile downwards and use it" . $lSep;
  659.         $msg .= $lSep;
  660.         $msg .= "Report bugs to <dev@phing.tigris.org>".$lSep;
  661.         print($msg);
  662.     }
  663.  
  664.     function printVersion() {
  665.         print(self::getPhingVersion()."\n");
  666.     }
  667.  
  668.     function getPhingVersion() {
  669.         $versionPath = self::getResourcePath("phing/etc/VERSION.TXT");
  670.         if ($versionPath === null) {
  671.             $versionPath = self::getResourcePath("etc/VERSION.TXT");
  672.         }
  673.         try { // try to read file
  674.             $buffer = null;
  675.             $file = new PhingFile($versionPath);
  676.             $reader = new FileReader($file);
  677.             $reader->readInto($buffer);
  678.             $buffer = trim($buffer);
  679.             //$buffer = "PHING version 1.0, Released 2002-??-??";
  680.             $phingVersion = $buffer;
  681.         } catch (IOException $iox) {
  682.             print("Can't read version information file\n");
  683.             throw new BuildException("Build failed");
  684.         }        
  685.         return $phingVersion;
  686.     }
  687.  
  688.     /**  Print the project description, if any */
  689.     function printDescription(Project $project) {
  690.         if ($project->getDescription() !== null) {
  691.             print($project->getDescription()."\n");
  692.         }
  693.     }
  694.  
  695.     /** Print out a list of all targets in the current buildfile */
  696.     function printTargets($project) {
  697.         // find the target with the longest name
  698.         $maxLength = 0;
  699.         $targets = $project->getTargets();
  700.         $targetNames = array_keys($targets);
  701.         $targetName = null;
  702.         $targetDescription = null;
  703.         $currentTarget = null;
  704.  
  705.         // split the targets in top-level and sub-targets depending
  706.         // on the presence of a description
  707.         
  708.         $subNames = array();
  709.         $topNameDescMap = array();
  710.         
  711.         foreach($targets as $currentTarget) {        
  712.             $targetName = $currentTarget->getName();
  713.             $targetDescription = $currentTarget->getDescription();            
  714.             
  715.             // subtargets are targets w/o descriptions
  716.             if ($targetDescription === null) {
  717.                 $subNames[] = $targetName;
  718.             } else {
  719.                 // topNames and topDescriptions are handled later
  720.                 // here we store in hash map (for sorting purposes)
  721.                 $topNameDescMap[$targetName] = $targetDescription;               
  722.                 if (strlen($targetName) > $maxLength) {
  723.                     $maxLength = strlen($targetName);
  724.                 }
  725.             }
  726.         }
  727.         
  728.         // Sort the arrays
  729.         sort($subNames); // sort array values, resetting keys (which are numeric)        
  730.         ksort($topNameDescMap); // sort the keys (targetName) keeping key=>val associations
  731.         
  732.         $topNames = array_keys($topNameDescMap);
  733.         $topDescriptions = array_values($topNameDescMap);
  734.  
  735.         $defaultTarget = $project->getDefaultTarget();
  736.  
  737.         if ($defaultTarget !== null && $defaultTarget !== "") {
  738.             $defaultName = array();
  739.             $defaultDesc = array();
  740.             $defaultName[] = $defaultTarget;
  741.  
  742.             $indexOfDefDesc = array_search($defaultTarget, $topNames, true);
  743.             if ($indexOfDefDesc !== false && $indexOfDefDesc >= 0) {
  744.                 $defaultDesc = array();
  745.                 $defaultDesc[] = $topDescriptions[$indexOfDefDesc];
  746.             }
  747.  
  748.             $this->_printTargets($defaultName, $defaultDesc, "Default target:", $maxLength);
  749.  
  750.         }
  751.         $this->_printTargets($topNames, $topDescriptions, "Main targets:", $maxLength);
  752.         $this->_printTargets($subNames, null, "Subtargets:", 0);
  753.     }    
  754.  
  755.     /**
  756.      * Writes a formatted list of target names with an optional description.
  757.      *
  758.      * @param array $names The names to be printed.
  759.      *              Must not be <code>null</code>.
  760.      * @param array $descriptions The associated target descriptions.
  761.      *                     May be <code>null</code>, in which case
  762.      *                     no descriptions are displayed.
  763.      *                     If non-<code>null</code>, this should have
  764.      *                     as many elements as <code>names</code>.
  765.      * @param string $heading The heading to display.
  766.      *                Should not be <code>null</code>.
  767.      * @param int $maxlen The maximum length of the names of the targets.
  768.      *               If descriptions are given, they are padded to this
  769.      *               position so they line up (so long as the names really
  770.      *               <i>are</i> shorter than this).
  771.      */
  772.     private function _printTargets($names, $descriptions, $heading, $maxlen) {
  773.         $lSep = self::getProperty("line.separator");
  774.         $spaces = '  ';
  775.         while (strlen($spaces) < $maxlen) {
  776.             $spaces .= $spaces;
  777.         }
  778.         $msg = "";
  779.         $msg .= $heading . $lSep;
  780.         $msg .= str_repeat("-",79) . $lSep;
  781.  
  782.         $total = count($names);
  783.         for($i=0; $i < $total; $i++) {
  784.             $msg .= " ";
  785.             $msg .= $names[$i];
  786.             if (!empty($descriptions)) {
  787.                 $msg .= substr($spaces, 0, $maxlen - strlen($names[$i]) + 2);
  788.                 $msg .= $descriptions[$i];
  789.             }
  790.             $msg .= $lSep;
  791.         }
  792.         if ($total > 0) {
  793.           print $msg . $lSep;
  794.         } 
  795.    }
  796.    
  797.    /**
  798.     * Import a dot-path notation class path.
  799.     * @param string $dotPath
  800.     * @param mixed $classpath String or object supporting __toString()
  801.     * @return string The unqualified classname (which can be instantiated).
  802.     * @throws BuildException - if cannot find the specified file
  803.     */
  804.    public static function import($dotPath, $classpath = null) {
  805.         
  806.         // first check to see that the class specified hasn't already been included.
  807.         // (this also handles case where this method is called w/ a classname rather than dotpath)
  808.         $classname = StringHelper::unqualify($dotPath);
  809.         if (class_exists($classname, false)) {
  810.             return $classname;
  811.         }
  812.         
  813.         $dotClassname = basename($dotPath);
  814.         $dotClassnamePos = strlen($dotPath) - strlen($dotClassname);
  815.         $classFile = strtr($dotClassname, '.', DIRECTORY_SEPARATOR) . ".php";
  816.         $path = substr_replace($dotPath, $classFile, $dotClassnamePos);
  817.         
  818.         Phing::__import($path, $classpath);
  819.         
  820.         return $classname;
  821.    }
  822.  
  823.    /**
  824.     * Import a PHP file
  825.     * @param string $path Path to the PHP file
  826.     * @param mixed $classpath String or object supporting __toString()
  827.     * @throws BuildException - if cannot find the specified file
  828.     */
  829.    public static function __import($path, $classpath = null) {
  830.         
  831.         if ($classpath) {
  832.         
  833.             // Apparently casting to (string) no longer invokes __toString() automatically.
  834.             if (is_object($classpath)) {
  835.                 $classpath = $classpath->__toString();
  836.             }
  837.             
  838.             // classpaths are currently additive, but we also don't want to just
  839.             // indiscriminantly prepand/append stuff to the include_path.  This means
  840.             // we need to parse current incldue_path, and prepend any
  841.             // specified classpath locations that are not already in the include_path.              
  842.             //
  843.             // NOTE:  the reason why we do it this way instead of just changing include_path
  844.             // and then changing it back, is that in many cases applications (e.g. Propel) will
  845.             // include/require class files from within method calls.  This means that not all
  846.             // necessary files will be included in this import() call, and hence we can't
  847.             // change the include_path back without breaking those apps.  While this method could
  848.             // be more expensive than switching & switching back (not sure, but maybe), it makes it
  849.             // possible to write far less expensive run-time applications (e.g. using Propel), which is
  850.             // really where speed matters more.
  851.             
  852.             $curr_parts = explode(PATH_SEPARATOR, ini_get('include_path'));
  853.             $add_parts = explode(PATH_SEPARATOR, $classpath);
  854.             $new_parts = array_diff($add_parts, $curr_parts);
  855.             if ($new_parts) {
  856.                 if (self::getMsgOutputLevel() === PROJECT_MSG_DEBUG) {
  857.                     print("Phing::import() prepending new include_path components: " . implode(PATH_SEPARATOR, $new_parts) . "\n");
  858.                 }
  859.                 ini_set('include_path', implode(PATH_SEPARATOR, array_merge($new_parts, $curr_parts)));
  860.             }
  861.         }
  862.         
  863.         $ret = include_once($path);        
  864.         
  865.         if ($ret === false) {
  866.             $e = new BuildException("Error importing $path");
  867.             if (self::getMsgOutputLevel() === PROJECT_MSG_DEBUG) {
  868.                 // We can't log this because listeners belong
  869.                 // to projects.  We'll just print it -- of course
  870.                 // that isn't very compatible w/ other frontends (but
  871.                 // there aren't any right now, so I'm not stressing)
  872.                 print("Error importing $path\n");
  873.                 print($e->getTraceAsString()."\n");
  874.             }        
  875.             throw $e;
  876.         }
  877.         
  878.         return;
  879.    }
  880.    
  881.    /**
  882.     * Looks on include path for specified file.
  883.     * @return string File found (null if no file found).
  884.     */
  885.    public static function getResourcePath($path) {
  886.         
  887.         if (self::$importPaths === null) {
  888.             $paths = ini_get("include_path");            
  889.             self::$importPaths = explode(PATH_SEPARATOR, ini_get("include_path"));
  890.         }
  891.         
  892.         $path = str_replace('\\', DIRECTORY_SEPARATOR, $path);
  893.         $path = str_replace('/', DIRECTORY_SEPARATOR, $path);
  894.  
  895.         foreach (self::$importPaths as $prefix) {
  896.             $foo_path = $prefix . DIRECTORY_SEPARATOR . $path;
  897.             if (file_exists($foo_path)) {
  898.                 return $foo_path;
  899.             }
  900.         }
  901.         
  902.         // Check for the property phing.home
  903.         $home_dir = self::getProperty('phing.home');
  904.         
  905.         if ($home_dir)
  906.         {
  907.             $home_path = $home_dir . DIRECTORY_SEPARATOR . $path;
  908.             
  909.             if (file_exists($home_path))
  910.             {
  911.                 return $home_path;
  912.             }
  913.         }
  914.         
  915.         // If we are using this via PEAR then check for the file in the data dir
  916.         // This is a bit of a hack, but works better than previous solution of assuming
  917.         // data_dir is on the include_path.
  918.         $data_dir = 'C:\php5\pear\data';
  919.         if ($data_dir{0} != '@') { // if we're using PEAR then the @ DATA-DIR @ token will have been substituted.
  920.             $data_path = $data_dir . DIRECTORY_SEPARATOR . $path;
  921.             if (file_exists($data_path)) {
  922.                    return $data_path;
  923.                }
  924.         }
  925.         
  926.         return null;
  927.    }
  928.    
  929.    // -------------------------------------------------------------------------------------------
  930.    // System-wide methods (moved from System class, which had namespace conflicts w/ PEAR System)
  931.    // -------------------------------------------------------------------------------------------
  932.              
  933.     /**
  934.      * Set System constants which can be retrieved by calling Phing::getProperty($propName).
  935.      * @return void
  936.      */
  937.     private static function setSystemConstants() {
  938.  
  939.         /*
  940.          * PHP_OS returns on
  941.          *   WindowsNT4.0sp6  => WINNT
  942.          *   Windows2000      => WINNT
  943.          *   Windows ME       => WIN32
  944.          *   Windows 98SE     => WIN32
  945.          *   FreeBSD 4.5p7    => FreeBSD
  946.          *   Redhat Linux     => Linux
  947.          *   Mac OS X          => Darwin
  948.          */
  949.         self::setProperty('host.os', PHP_OS);
  950.         
  951.         // this is used by some tasks too
  952.         self::setProperty('os.name', PHP_OS);
  953.         
  954.         // it's still possible this won't be defined,
  955.         // e.g. if Phing is being included in another app w/o
  956.         // using the phing.php script.
  957.         if (!defined('PHP_CLASSPATH')) {
  958.             define('PHP_CLASSPATH', get_include_path());
  959.         }
  960.         
  961.         self::setProperty('php.classpath', PHP_CLASSPATH);
  962.  
  963.         // try to determine the host filesystem and set system property
  964.         // used by Fileself::getFileSystem to instantiate the correct
  965.         // abstraction layer
  966.  
  967.         switch (strtoupper(PHP_OS)) {
  968.             case 'WINNT':
  969.                 self::setProperty('host.fstype', 'WINNT');
  970.                 break;
  971.             case 'WIN32':
  972.                 self::setProperty('host.fstype', 'WIN32');
  973.                 break;
  974.             default:
  975.                 self::setProperty('host.fstype', 'UNIX');
  976.                 break;
  977.         }
  978.  
  979.         self::setProperty('php.version', PHP_VERSION);
  980.         self::setProperty('user.home', getenv('HOME'));
  981.         self::setProperty('application.startdir', getcwd());
  982.         self::setProperty('line.separator', "\n");
  983.  
  984.         // try to detect machine dependent information
  985.         $sysInfo = array();
  986.         if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN' && function_exists("posix_uname")) {
  987.               $sysInfo = posix_uname();
  988.         } else {
  989.               $sysInfo['nodename'] = php_uname('n');
  990.               $sysInfo['machine']= php_uname('m') ;
  991.               //this is a not so ideal substition, but maybe better than nothing
  992.               $sysInfo['domain'] = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : "unknown";
  993.               $sysInfo['release'] = php_uname('r');
  994.               $sysInfo['version'] = php_uname('v');
  995.         }              
  996.      
  997.  
  998.         self::setProperty("host.name", isset($sysInfo['nodename']) ? $sysInfo['nodename'] : "unknown");
  999.         self::setProperty("host.arch", isset($sysInfo['machine']) ? $sysInfo['machine'] : "unknown");
  1000.         self::setProperty("host.domain",isset($sysInfo['domain']) ? $sysInfo['domain'] : "unknown");
  1001.         self::setProperty("host.os.release", isset($sysInfo['release']) ? $sysInfo['release'] : "unknown");
  1002.         self::setProperty("host.os.version", isset($sysInfo['version']) ? $sysInfo['version'] : "unknown");
  1003.         unset($sysInfo);
  1004.     }
  1005.     
  1006.     /**
  1007.      * This gets a property that was set via command line or otherwise passed into Phing.
  1008.      * "Defined" in this case means "externally defined".  The reason this method exists is to
  1009.      * provide a public means of accessing commandline properties for (e.g.) logger or listener 
  1010.      * scripts.  E.g. to specify which logfile to use, PearLogger needs to be able to access
  1011.      * the pear.log.name property.
  1012.      * 
  1013.      * @param string $name
  1014.      * @return string value of found property (or null, if none found).
  1015.      */
  1016.     public static function getDefinedProperty($name) {
  1017.         return self::$definedProps->getProperty($name);
  1018.     }
  1019.     
  1020.     /**
  1021.      * This sets a property that was set via command line or otherwise passed into Phing.
  1022.      * 
  1023.      * @param string $name
  1024.      * @return string value of found property (or null, if none found).
  1025.      */
  1026.     public static function setDefinedProperty($name, $value) {
  1027.         return self::$definedProps->setProperty($name, $value);
  1028.     }
  1029.     
  1030.     /**
  1031.      * Returns property value for a System property.
  1032.      * System properties are "global" properties like line.separator,
  1033.      * and user.dir.  Many of these correspond to similar properties in Java
  1034.      * or Ant.
  1035.      * 
  1036.      * @param string $paramName
  1037.      * @return string Value of found property (or null, if none found).
  1038.      */
  1039.     public static function getProperty($propName) {
  1040.     
  1041.         // some properties are detemined on each access
  1042.         // some are cached, see below
  1043.  
  1044.         // default is the cached value:
  1045.         $val = isset(self::$properties[$propName]) ? self::$properties[$propName] : null;
  1046.     
  1047.         // special exceptions        
  1048.         switch($propName) {
  1049.             case 'user.dir':
  1050.                 $val = getcwd();
  1051.             break;            
  1052.         }
  1053.         
  1054.         return $val;
  1055.     }
  1056.  
  1057.     /** Retuns reference to all properties*/
  1058.     public static function &getProperties() {
  1059.         return self::$properties;
  1060.     }
  1061.  
  1062.     public static function setProperty($propName, $propValue) {    
  1063.         $propName = (string) $propName;
  1064.         $oldValue = self::getProperty($propName);
  1065.         self::$properties[$propName] = $propValue;
  1066.         return $oldValue;
  1067.     }
  1068.     
  1069.     public static function currentTimeMillis() {
  1070.         list($usec, $sec) = explode(" ",microtime());
  1071.         return ((float)$usec + (float)$sec);
  1072.     }
  1073.     
  1074.     /**
  1075.      * Sets the include path based on PHP_CLASSPATH constant (set in phing.php).
  1076.      * @return void
  1077.      */
  1078.     private static function setIncludePaths() {
  1079.         $success = false;
  1080.         
  1081.         if (defined('PHP_CLASSPATH')) {
  1082.             $success = ini_set('include_path', PHP_CLASSPATH);
  1083.         } else {
  1084.             // don't do anything, just assume that include_path has been properly set.
  1085.             $success = true;
  1086.         }
  1087.         
  1088.         if ($success === false) {
  1089.             print("SYSTEM FAILURE: Could not set PHP include path\n");
  1090.             self::halt(-1);
  1091.         }
  1092.     }
  1093.     
  1094.     /**
  1095.      * Sets PHP INI values that Phing needs.
  1096.      * @return void
  1097.      */
  1098.     private static function setIni() {
  1099.         error_reporting(E_ALL);
  1100.         set_time_limit(0);
  1101.         ini_set('magic_quotes_gpc', 'off');
  1102.         ini_set('short_open_tag', 'off');
  1103.         ini_set('default_charset', 'iso-8859-1');
  1104.         ini_set('register_globals', 'off');
  1105.         ini_set('allow_call_time_pass_reference', 'on');
  1106.         
  1107.         // should return memory limit in MB  
  1108.         $mem_limit = (int) ini_get('memory_limit');
  1109.         if ($mem_limit < 32) {
  1110.             ini_set('memory_limit', '32M'); // nore: this may need to be higher for many projects
  1111.         }        
  1112.     }
  1113.  
  1114.     /**
  1115.      * Returns reference to Timer object.
  1116.      * @return Timer
  1117.      */
  1118.     public static function getTimer() {
  1119.         if (self::$timer === null) {
  1120.             include_once 'phing/system/util/Timer.php';
  1121.             self::$timer= new Timer();
  1122.         }
  1123.         return self::$timer;
  1124.     }
  1125.         
  1126.      /**
  1127.      * Start up Phing.
  1128.      * Sets up the Phing environment -- does NOT initiate the build process.
  1129.      * @return void
  1130.      */
  1131.     public static function startup() {
  1132.        
  1133.         register_shutdown_function(array('Phing', 'shutdown'));
  1134.  
  1135.         // some init stuff
  1136.         self::getTimer()->start();
  1137.  
  1138.         self::setSystemConstants();
  1139.         self::setIncludePaths();
  1140.         self::setIni();
  1141.     }
  1142.     
  1143.     /**
  1144.      * Halts the system.
  1145.      * @see shutdown()
  1146.      */
  1147.     public static function halt($code=0) {        
  1148.         self::shutdown($code);        
  1149.     }
  1150.  
  1151.     /**
  1152.      * Stops timers & exits.
  1153.      * @return void
  1154.      */
  1155.     public static function shutdown($exitcode = 0) {
  1156.         //print("[AUTOMATIC SYSTEM SHUTDOWN]\n");
  1157.         self::getTimer()->stop();
  1158.         exit($exitcode); // final point where everything stops
  1159.     }
  1160.     
  1161. }
  1162.